package editor.animation;

import javax.swing.*;
import java.awt.*;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.image.BufferedImage;

public class SelectableImage extends JPanel {
	public SelectableImage() {
		setBackground(Color.BLACK);
		drawingRect = false;

		addMouseListener(new MouseListener() {
			@Override
			public void mouseClicked(MouseEvent e) {}

			@Override
			public void mousePressed(MouseEvent e) {
				if (!selectingEnabled) {
					return;
				}
				int x = gridAlignX(e.getX());
				int y = gridAlignY(e.getY());
				if (e.getButton() == MouseEvent.BUTTON1) {
					drawingRect = true;
					rectX0 = rectX1 = x;
					rectY0 = rectY1 = y;
					repaint();
				} else if (e.getButton() == MouseEvent.BUTTON3) {
					crossX = x;
					crossY = y;
					crossSet = true;
					if (listener != null) {
						listener.onCrossSelected(crossX, crossY);
					}
					repaint();
				}
			}

			@Override
			public void mouseReleased(MouseEvent e) {
				if (!selectingEnabled) {
					return;
				}
				if (e.getButton() == MouseEvent.BUTTON1) {
					drawingRect = false;
					if (listener != null) {
						listener.onRectangleSelected(rectX0, rectY0, rectX1, rectY1);
					}
				}
			}

			@Override
			public void mouseEntered(MouseEvent e) {}

			@Override
			public void mouseExited(MouseEvent e) {}
		});

		addMouseMotionListener(new MouseMotionListener() {
			@Override
			public void mouseDragged(MouseEvent e) {
				if (drawingRect) {
					rectX1 = gridAlignX(e.getX());
					rectY1 = gridAlignY(e.getY());
					repaint();
				}
			}

			@Override
			public void mouseMoved(MouseEvent e) {}
		});

		selectingEnabled = true;
	}

	public void setImage(BufferedImage image) {
		this.image = image;
		if (image == null) {
			setPreferredSize(new Dimension(0, 0));
			invImage = null;
		} else {
			setPreferredSize(new Dimension(image.getWidth(), image.getHeight()));
			invImage = new BufferedImage(image.getWidth(), image.getHeight(), image.getType());
			for (int y = 0; y < image.getHeight(); ++y) {
				for (int x = 0; x < image.getWidth(); ++x) {
					int rgba = image.getRGB(x, y);
					// pre-blend alpha blended against black
					int a = (rgba >> 24) & 0xFF;
					int r = (rgba >> 16) & 0xFF;
					int g = (rgba >>  8) & 0xFF;
					int b = (rgba      ) & 0xFF;
					float af = (float) a / 255.0f;
					float rf = af * (float) r / 255.0f;
					float gf = af * (float) g / 255.0f;
					float bf = af * (float) b / 255.0f;
					r = 255 - (int) (rf * 255.0f) & 0xFF;
					g = 255 - (int) (gf * 255.0f) & 0xFF;
					b = 255 - (int) (bf * 255.0f) & 0xFF;
					invImage.setRGB(x, y, 0xFF000000 | (r << 16) | (g << 8) | b);
				}
			}
		}
		repaint();
	}

	public void setGridEnabled(boolean e) {
		if (grid != e) {
			grid = e;
			repaint();
		}
	}

	public void setGridSize(int x, int y) {
		if (gridX != x || gridY != y) {
			gridX = x;
			gridY = y;
			repaint();
		}
	}

	public interface SelectionListener {
		public void onRectangleSelected(int x0, int y0, int x1, int y1);
		public void onCrossSelected(int x, int y);
	}

	public void setSelectionListener(SelectionListener listener) {
		this.listener = listener;
	}

	public void setSelectingEnabled(boolean enabled) {
		selectingEnabled = enabled;
		if (!enabled) {
			drawingRect = false;
		}
	}

	public void setRectangleSelection(int x0, int y0, int x1, int y1, boolean call) {
		rectX0 = x0;
		rectY0 = y0;
		rectX1 = x1;
		rectY1 = y1;
		if (call && listener != null) {
			listener.onRectangleSelected(rectX0, rectY0, rectX1, rectY1);
		}
		repaint();
	}

	public void clearRectangleSelection() {
		rectX0 = rectX1 = rectY0 = rectY1 = 0;
		repaint();
	}

	public void setCrossSelection(int x, int y, boolean call) {
		crossX = x;
		crossY = y;
		crossSet = true;
		if (call && listener != null) {
			listener.onCrossSelected(crossX, crossY);
		}
		repaint();
	}

	public void clearCrossSelection() {
		crossSet = false;
		repaint();
	}

	private int gridAlignX(int x) {
		if (!grid) {
			return x;
		} else {
			return ((x + gridX/2) / gridX) * gridX;
		}
	}

	private int gridAlignY(int y) {
		if (!grid) {
			return y;
		} else {
			return ((y + gridY/2) / gridY) * gridY;
		}
	}

	@Override
	protected void paintComponent(Graphics g) {
		super.paintComponent(g);

		if (image == null) {
			return;
		}

		g = g.create();
		g.setColor(Color.WHITE);
		g.drawImage(image, 0, 0, null);
		if (grid) {
			if (gridX > 0 && gridY > 0) {
				for (int y = 0; y < image.getHeight(); y += gridY) {
					for (int x = 0; x < image.getWidth(); x += gridX) {
						g.drawImage(invImage, x, y, x+1, y+1, x, y, x+1, y+1, null);
					}
				}
			}
		}
		if (rectX0 != rectX1 && rectY0 != rectY1) {
			// horiz lines
			g.drawImage(invImage, rectX0, rectY0, rectX1-1, rectY0+1, rectX0, rectY0, rectX1-1, rectY0+1, null);
			g.drawImage(invImage, rectX0, rectY1-1, rectX1-1, rectY1, rectX0, rectY1-1, rectX1-1, rectY1, null);

			// vert lines
			g.drawImage(invImage, rectX0, rectY0, rectX0+1, rectY1-1, rectX0, rectY0, rectX0+1, rectY1-1, null);
			g.drawImage(invImage, rectX1-1, rectY0, rectX1, rectY1-1, rectX1-1, rectY0, rectX1, rectY1-1, null);
		}
		if (crossSet) {
			final int SIZE = 4;
			g.drawImage(invImage, crossX, crossY, crossX-SIZE, crossY+1, crossX, crossY, crossX-SIZE, crossY+1, null);
			g.drawImage(invImage, crossX+1, crossY, crossX+1+SIZE, crossY+1, crossX+1, crossY, crossX+1+SIZE, crossY+1, null);
			g.drawImage(invImage, crossX, crossY, crossX+1, crossY-SIZE, crossX, crossY, crossX+1, crossY-SIZE, null);
			g.drawImage(invImage, crossX, crossY+1, crossX+1, crossY+1+SIZE, crossX, crossY+1, crossX+1, crossY+1+SIZE, null);
		}
	}

	private BufferedImage image;
	private BufferedImage invImage;
	private boolean grid = false;
	private int gridX;
	private int gridY;

	private boolean drawingRect;
	private int rectX0, rectY0, rectX1, rectY1;
	private boolean crossSet;
	private int crossX, crossY;

	private SelectionListener listener;
	private boolean selectingEnabled;
}
